# -*- coding:binary -*-
require 'spec_helper'

RSpec.describe Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base do
  let(:params) {
    {
      realm: 'demo.local',
      hostname: 'mock_hostname',
      username: 'mock_username',
      password: 'mock_password',
      host: '127.0.0.1',
      port: 88,
      timeout: 25,
      framework: instance_double(::Msf::Framework),
      framework_module: instance_double(::Msf::Module)
    }
  }

  subject do
    described_class.new(**params)
  end

  describe '#connect' do
    before(:each) do
      allow(params[:framework_module]).to receive(:framework)
      allow(params[:framework_module]).to receive(:print_status)
      allow(params[:framework_module]).to receive(:vprint_status)
    end

    context 'when host is nil' do
      it 'resolves it to a hostname' do
        expect(::Rex::Socket).to receive(:getresources).with("_kerberos._tcp.#{params[:realm]}", :SRV).and_return(['mock_host'])
        instance = described_class.new(**params.merge(host: nil))
        instance.connect
        expect(instance.host).to eq('mock_host')
      end

      it 'raises a KerberosError on failure' do
        expect(::Rex::Socket).to receive(:getresources).with("_kerberos._tcp.#{params[:realm]}", :SRV).and_return([])
        instance = described_class.new(**params.merge(host: nil))
        expect { instance.connect }.to raise_error(::Rex::Proto::Kerberos::Model::Error::KerberosError)
      end
    end
  end

  describe "#validate_response!" do
    context 'when the response is a success' do
      let(:response) do
        "\xa1\x14\x30\x12\xa0\x03\x0a\x01\x00\xa1\x0b\x06\x09\x2a\x86\x48" \
        "\x82\xf7\x12\x01\x02\x02"
      end

      it 'returns true' do
        expect(subject.validate_response!(response)).to be true
      end
    end

    context 'when the response is invalid ASN1' do
      let(:response) do
        'abcd'
      end

      it 'raises a Kerberos decoding exception' do
        expect { subject.validate_response!(response) }.to raise_error(::Rex::Proto::Kerberos::Model::Error::KerberosDecodingError, /Invalid GSS/)
      end
    end

    context 'when the response is accept-incomplete and contains a kerberos error' do
      let(:response) do
        "\xa1\x81\x89\x30\x81\x86\xa0\x03\x0a\x01\x01\xa1\x0b\x06\x09\x2a" \
        "\x86\x48\x82\xf7\x12\x01\x02\x02\xa2\x72\x04\x70\x60\x6e\x06\x09" \
        "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03\x00\x7e\x5f\x30\x5d\xa0" \
        "\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa4\x11\x18\x0f\x32\x30\x32" \
        "\x32\x30\x36\x32\x39\x32\x33\x32\x32\x31\x31\x5a\xa5\x05\x02\x03" \
        "\x0e\x45\xd0\xa6\x03\x02\x01\x3c\xa9\x0c\x1b\x0a\x41\x44\x46\x33" \
        "\x2e\x4c\x4f\x43\x41\x4c\xaa\x11\x30\x0f\xa0\x03\x02\x01\x01\xa1" \
        "\x08\x30\x06\x1b\x04\x64\x63\x33\x24\xac\x11\x04\x0f\x30\x0d\xa1" \
        "\x03\x02\x01\x01\xa2\x06\x04\x04\x6d\x00\x00\xc0"
      end

      it 'raises a Kerberos exception' do
        expect { subject.validate_response!(response) }.to raise_error(::Rex::Proto::Kerberos::Model::Error::KerberosError, /Failed to negotiate/)
      end
    end
  end
end
